package cn.lingyangwl.agile.pay.common.service.impl;

import cn.lingyangwl.agile.pay.common.assembly.RefundAssembly;
import cn.lingyangwl.framework.core.page.PageModel;
import cn.lingyangwl.framework.core.page.TableSupport;
import cn.lingyangwl.framework.data.mate.annotations.FieldBindHandle;
import cn.lingyangwl.framework.mybatis.plus.utils.PageUtil;
import cn.lingyangwl.framework.tool.core.CollectionUtils;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import cn.hutool.core.date.DateTime;
import cn.lingyangwl.agile.pay.model.rqrs.RefundOrderQL;
import cn.lingyangwl.agile.pay.model.rqrs.pay.refund.RefundOrderRS;
import cn.lingyangwl.agile.pay.model.entity.PayWay;
import cn.lingyangwl.agile.pay.model.entity.RefundOrder;
import cn.lingyangwl.agile.pay.common.manager.PayWayManager;
import cn.lingyangwl.agile.pay.common.mapper.PayOrderMapper;
import cn.lingyangwl.agile.pay.common.mapper.RefundOrderMapper;
import cn.lingyangwl.agile.pay.common.service.RefundOrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author shenguangyang
 */
@Service
public class RefundOrderServiceImpl extends ServiceImpl<RefundOrderMapper, RefundOrder> implements RefundOrderService {
    @Resource
    protected PayOrderMapper payOrderMapper;
    @Resource
    private RefundAssembly refundAssembly;
    @Resource
    private PayWayManager payWayManager;

    @Override
    public BigDecimal sumSuccessRefundAmount(String payOrderNo) {
        return this.baseMapper.sumSuccessRefundAmount(payOrderNo);
    }

    /**
     * 更新退款单状态 退款单生成 --> 退款中
     */
    @Override
    public boolean updateInit2Ing(String refundOrderId, String channelOrderNo) {
        RefundOrder updateRecord = new RefundOrder();
        updateRecord.setStatus(RefundOrder.STATE_ING);
        updateRecord.setChannelOrderNo(channelOrderNo);

        return update(updateRecord, new LambdaUpdateWrapper<RefundOrder>()
                .eq(RefundOrder::getRefundOrderId, refundOrderId).eq(RefundOrder::getStatus, RefundOrder.STATE_INIT));
    }

    /**
     * 更新退款单状态  退款中 --> 退款成功
     */
    @Transactional
    @Override
    public boolean updateIng2Success(String refundOrderId, String channelOrderNo) {

        RefundOrder updateRecord = new RefundOrder();
        updateRecord.setStatus(RefundOrder.STATE_SUCCESS);
        updateRecord.setChannelOrderNo(channelOrderNo);
        updateRecord.setSuccessTime(new Date());

        // 1. 更新退款订单表数据
        if (!update(updateRecord, RefundOrder.lqw().eq(RefundOrder::getRefundOrderId, refundOrderId)
                .eq(RefundOrder::getStatus, RefundOrder.STATE_ING))
        ) {
            return false;
        }

        // 2. 更新订单表数据（更新退款次数,退款状态,如全额退款更新支付状态为已退款）
        RefundOrder refundOrder = getOne(RefundOrder.lqw().select(RefundOrder::getPayOrderNo, RefundOrder::getRefundAmount).eq(RefundOrder::getRefundOrderId, refundOrderId));
        int updateCount = payOrderMapper.updateRefundAmountAndCount(refundOrder.getPayOrderNo(), refundOrder.getRefundAmount());
        if (updateCount <= 0) {
            throw new BizException("更新订单数据异常");
        }

        return true;
    }


    /**
     * 更新退款单状态  退款中 --> 退款失败
     */
    @Transactional
    @Override
    public boolean updateIng2Fail(String refundOrderId, String channelOrderNo, String channelErrCode, String channelErrMsg) {

        RefundOrder updateRecord = new RefundOrder();
        updateRecord.setStatus(RefundOrder.STATE_FAIL);
        updateRecord.setErrCode(channelErrCode);
        updateRecord.setErrMsg(channelErrMsg);
        updateRecord.setChannelOrderNo(channelOrderNo);

        return update(updateRecord, new LambdaUpdateWrapper<RefundOrder>()
                .eq(RefundOrder::getRefundOrderId, refundOrderId).eq(RefundOrder::getStatus, RefundOrder.STATE_ING));
    }


    /**
     * 更新退款单状态  退款中 --> 退款成功/退款失败
     */
    @Transactional
    @Override
    public boolean updateIng2SuccessOrFail(String refundOrderId, String updateState,
                                           String channelOrderNo, String channelErrCode, String channelErrMsg) {

        if (Objects.equals(updateState, RefundOrder.STATE_ING)) {
            return true;
        } else if (Objects.equals(updateState, RefundOrder.STATE_SUCCESS)) {
            return updateIng2Success(refundOrderId, channelOrderNo);
        } else if (Objects.equals(updateState, RefundOrder.STATE_FAIL)) {
            return updateIng2Fail(refundOrderId, channelOrderNo, channelErrCode, channelErrMsg);
        }
        return false;
    }


    /**
     * 更新退款单为 关闭状态
     */
    public Integer updateOrderExpired() {

        RefundOrder refundOrder = new RefundOrder();
        refundOrder.setStatus(RefundOrder.STATE_CLOSED);

        return baseMapper.update(refundOrder,
                RefundOrder.lqw()
                        .in(RefundOrder::getStatus, Arrays.asList(RefundOrder.STATE_INIT, RefundOrder.STATE_ING))
                        .le(RefundOrder::getExpiredTime, new Date())
        );
    }

    @Override
    @FieldBindHandle
    public IPage<RefundOrderRS> pageRefundOrder(RefundOrderQL req) {
        PageModel page = TableSupport.buildPageRequest();
        LocalDateTime beginTime = Optional.ofNullable(page.getBeginDate()).map(DateTime::toLocalDateTime).orElse(null);
        LocalDateTime endTime = Optional.ofNullable(page.getEndDate()).map(DateTime::toLocalDateTime).orElse(null);
        LambdaQueryWrapper<RefundOrder> lqw = RefundOrder.lqw()
                .eq(StringUtils.isNotEmpty(req.getMchRefundNo()), RefundOrder::getMchRefundNo, req.getMchRefundNo())
                .eq(StringUtils.isNotEmpty(req.getRefundOrderId()), RefundOrder::getRefundOrderId, req.getRefundOrderId())
                .eq(StringUtils.isNotEmpty(req.getPayOrderNo()), RefundOrder::getPayOrderNo, req.getPayOrderNo())
                .eq(StringUtils.isNotEmpty(req.getMchType()), RefundOrder::getMchType, req.getMchType())
                .eq(StringUtils.isNotEmpty(req.getMchNo()), RefundOrder::getMchNo, req.getMchNo())
                .eq(StringUtils.isNotEmpty(req.getAppId()), RefundOrder::getAppId, req.getAppId())
                .eq(StringUtils.isNotEmpty(req.getRefundStatus()), RefundOrder::getStatus, req.getRefundStatus())
                .ge(Objects.nonNull(beginTime), RefundOrder::getCreatedAt, beginTime)
                .le(Objects.nonNull(endTime), RefundOrder::getCreatedAt, endTime)
                .eq(StringUtils.isNotEmpty(req.getMchType()), RefundOrder::getMchType, req.getMchType());

        lqw.and(StringUtils.isNotEmpty(req.getUnionOrderNo()), wr -> {
            wr.eq(RefundOrder::getPayOrderNo, req.getUnionOrderNo())
                    .or().eq(RefundOrder::getRefundOrderId, req.getUnionOrderNo())
                    .or().eq(RefundOrder::getChannelPayOrderNo, req.getUnionOrderNo())
                    .or().eq(RefundOrder::getMchRefundNo, req.getUnionOrderNo());
        }).orderByDesc(RefundOrder::getCreatedAt);

        IPage<RefundOrder> pageResp = this.page(PageUtil.to(page), lqw);
        Set<String> wayCodes = pageResp.getRecords().stream().map(RefundOrder::getWayCode).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(wayCodes)) {
            return new Page<>();
        }
        Map<String, String> payWayMap = payWayManager.list(PayWay.lqw().in(PayWay::getWayCode, wayCodes)).stream()
                .collect(Collectors.toMap(PayWay::getWayCode, PayWay::getWayName));
        return PageUtil.to(pageResp, e -> {
            String wayCodeValue = payWayMap.getOrDefault(e.getWayCode(), "");
            RefundOrderRS refundOrderRS = refundAssembly.toRefundOrderRS(e);
            refundOrderRS.setWayCodeValue(wayCodeValue);
            return refundOrderRS;
        });
    }

    @Override
    public RefundOrder getMchOrder(String mchNo, String mchRefundNo, String refundOrderNo) {
        LambdaQueryWrapper<RefundOrder> lqw = RefundOrder.lqw()
                .eq(StringUtils.isNotEmpty(mchNo), RefundOrder::getMchNo, mchNo)
                .eq(StringUtils.isNotEmpty(mchRefundNo), RefundOrder::getMchRefundNo, mchRefundNo)
                .eq(StringUtils.isNotEmpty(refundOrderNo), RefundOrder::getRefundOrderId, refundOrderNo);
        return this.getOne(lqw);
    }
}
